package com.zkh.myframe.core;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zkh.myframe.annotation.Arguments;
import com.zkh.myframe.annotation.Async;
import com.zkh.myframe.annotation.RequestMethod;
import com.zkh.myframe.exception.BaseException;
import com.zkh.myframe.validate.ParamValidator;
import com.zkh.myutils.cache.CacheManager;
import com.zkh.myutils.code.Charsets;
import com.zkh.myutils.code.CoderUtils;
import com.zkh.myutils.io.file.FileUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.net.upload.Uploader;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.Utils;
import com.zkh.myutils.utils.UtilsException;

/**
 * Controller处理器，用来处理Controller方法
 */
class ControllerHandler {
	/**
	 * 异步缓存key
	 */
	private final static String ASYNC_CACHE_KEY = "cacheForAsyncRequest";

	/**
	 * 访问Controller的核心方法，由链对象自动调用
	 * @param request ServletRequest对象
	 * @param response ServletResponse对象
	 * @param controllerName URLPattern和Controller的ClassName
	 */
	static void controllerAccess(HttpServletRequest request, HttpServletResponse response, Object[] controllerName)
			throws Exception {
		// 获取URI
		String uri = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("//+", "/");
		// 检查是否是首页访问，且有无首页拦截
		if ("/".equals(uri) && !FrameConfigure.getFirstPage().isEmpty()) {
			uri = FrameConfigure.getFirstPage();
		}
		// 检查并调整url的后缀
		uri = checkUrlSuffix(uri);
		// 获取URLPattern
		String urlPattern = (String) controllerName[0];

		// 获取方法，默认调用index方法
		String method = "index";
		// 获取方法名及其后面的参数
		String methods = CoderUtils.decodeURL(uri.substring(urlPattern.length()), FrameConfigure.getGlobalCharset());
		// 参数集合
		List<String> args = new ArrayList<>();
		// 判断pattern以*号结尾
		if (methods.length() > 1) {
			// 去掉方法参数前的/
			methods = methods.substring(1);
			// 获取方法和参数集合
			args.addAll(Arrays.asList(methods.split("/")));
			// 获取方法名
			method = args.get(0);
			// 移除方法名，剩余的都是参数
			args.remove(0);
		}
		// 访问Controller方法
		accessControllerMethod(method, request, response, args, (Class<?>) controllerName[1]);
	}

	/**
	 * 访问Controller方法
	 * @param method 本次调用的Controller方法名
	 * @param args 解析的请求参数
	 */
	private static void accessControllerMethod(String method, HttpServletRequest request, HttpServletResponse response, List<String> args, Class<?> clazz) throws Exception {
		// 使用反射调用方法，方法参数为Adapter适配器类
		try {
			// 实例化适配器类
			Adapter<ParamValidator> adapter = new Adapter<>(request, response);
			// 获取Controller实例
			Object instance = FrameUtils.getResourceClass(clazz, FrameConfigure.getResourceSingleton());
			/**↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 使用反射获取请求主方法(开始) ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓**/
			// 获取反射方法对象
			Method fMethod;
			boolean withAdapter = true;
			// 获取方法反射
			try {
				fMethod = clazz.getMethod(method, Adapter.class);
			} catch (NoSuchMethodException e) {
				// 获取无参反射方法对象
				fMethod = clazz.getMethod(method);
				withAdapter = false;
			}
			// 请求方式注解
			RequestMethod requestMethod = fMethod.getAnnotation(RequestMethod.class);
			// 存在注解，且与设置的请求方式不相同，返回404
			if (requestMethod != null && !requestMethod.method().equalsIgnoreCase(request.getMethod())) {
				// 返回404
				adapter.setResult(404);
				// 处理后续步骤，如：跳转，重定向，发送HTML或返送JSON数据等
				adapter.doResult();
				return;
			}
			// 有Adapter入参，则处理参数
			if (withAdapter) {
				// 解析方法上的参数注解。
				if (!args.isEmpty()) {
					parseAnnotation(fMethod, args, adapter);
				}
				// 格式化参数
				formatAguments(adapter);
			}
			// 检查异步
			Async async = fMethod.getAnnotation(Async.class);
			// 存在异步注解
			if (async != null) {
				// 处理异步请求，并保存响应结果
				adapter.setResult(dealAsyncRequest(instance, async, adapter, fMethod, withAdapter));
			} else {
				// 保存HttpServletRequest和HttpServletResponse对象
				FrameUtils.getServletRequestContainer().set(request);
				FrameUtils.getServletResponseContainer().set(response);
				// 验证器校验参数
				validParameter(adapter, fMethod.getParameters());
				// 验证器返回JSON
				boolean rtnJSON = false;
				// POST请求，且校验未通过，则直接返回JSON
				if (!adapter.getParamValidateErrorMsg().isEmpty() && adapter.isTargetMethod("POST")) {
					// 组合错误信息
					adapter.putMap("message", String.join("；", adapter.getParamValidateErrorMsg().values()));
					adapter.putMap("code", "9999");
					// 保存返回的JSON对象
					adapter.setResultWithMap();
					// 标注直接返回
					rtnJSON = true;
				}
				if (!rtnJSON) {
					// 调用反射获得的方法，如果有返回值，则保存返回值
					Optional.ofNullable(withAdapter ? fMethod.invoke(instance, adapter) : fMethod.invoke(instance))
							.ifPresent(adapter::setResult);
				}
			}
			// 处理后续步骤，如：跳转，重定向，发送HTML或返送JSON数据等
			adapter.doResult();
		} catch (NoSuchMethodException e) {
			// 方法不存在，释放
			throw new BaseException(999);
		} catch (InvocationTargetException e) {
			// 获取抛出的自定义异常
			Throwable ex = e.getTargetException();
			// 错误页面
			String excepPage = FrameConfigure.getBaseExceptionPage();
			// BaseException
			if (ex instanceof BaseException && !StringUtils.isEmpty(excepPage)) {
				// 捕获自定义异常
				request.setAttribute("e", ex);
				request.getRequestDispatcher(excepPage).forward(request, response);
			} else {
				throw ex instanceof Exception ? (Exception) ex : new Exception(ex);
			}
		} finally {
			// 移除HttpServletRequest和HttpServletResponse对象
			FrameUtils.getServletRequestContainer().remove();
			FrameUtils.getServletResponseContainer().remove();
		}
	}
	
	/**
	 * 验证器校验入参
	 */
	@SuppressWarnings("unchecked")
	private static void validParameter(Adapter<ParamValidator> adapter, Parameter[] params) throws ClassNotFoundException{
		//获取参数类型以获取泛型类型
		Type type = params.length>0 ? params[0].getParameterizedType() : null;
		//使用了泛型
		if(type instanceof ParameterizedType) {
			//泛型类型
			String geneType = ((ParameterizedType) type).getActualTypeArguments()[0].getTypeName();
			//不等于？
			if(!"?".equals(geneType)) {
				//初始化泛型
				adapter.setParamValidatorClass((Class<ParamValidator>) Class.forName(geneType));
				//转换参数并验证
				try {
					//参数赋值并执行验证器检查
					adapter.validateErrorMsg = ValidatorChecker.execParamValidatorCheck(adapter);
				}catch(Exception e) {
					//打印异常日志
					Logger.error(e);
					//异常消息
					String errorMsg = (e instanceof UtilsException ? "" : "转换并验证参数时出现错误：") + e.getMessage();
					//保存异常
					adapter.validateErrorMsg.put("otherError", errorMsg);
				}
			}
		}
	}
	
	/**
	 * 处理异步请求
	 * @param instance Controller实例
	 * @param async 异步注解
	 * @param adapter 适配器
	 * @param method 反射方法对象
	 * @param withAdapter 是否含Adapter入参
	 */
	@SuppressWarnings("unchecked")
	private static Map<String, Object> dealAsyncRequest(Object instance, Async async, Adapter<ParamValidator> adapter, Method method, boolean withAdapter){
		//获取JSON参数
		Map<String, Object> jsonMap = adapter.isJSONParam() ? adapter.getJsonParamToEntity(Map.class) : null;
		//获取异步ID
		String asyncId = jsonMap==null ? adapter.getParameter("asyncId") : (String) jsonMap.get("asyncId");
		//响应状态变量
		int state;
		//响应结果
		Map<String, Object> result = new HashMap<>();
		//没有异步ID，记为首次访问
		if(StringUtils.isEmpty(asyncId)) {
			//生成asyncId
			asyncId = Utils.getUUID();
			//写入状态（计算中）
			state = 0;
			//提交异步任务
			asyncSubmit(asyncId, instance, async, adapter, method, withAdapter);
			//写入缓存
			CacheManager.set(asyncId, state, async.cacheTime() * 60, ASYNC_CACHE_KEY);
		}//有异步ID
		else {
			//获取状态（无记录默认-1）
			state = Assert.getIfNotNull((Integer) CacheManager.get(asyncId, ASYNC_CACHE_KEY), st->st, -1);
			//如果计算完成，获取响应内容（1：计算完成；2：验证器校验失败；3：计算失败抛出异常）
			if(state==1 || state==2 || state==3) {
				//获取结果
				Object res = CacheManager.get(asyncId+"Data", ASYNC_CACHE_KEY);
				//正常计算完成
				if(state==1) {
					result.put("data", res);
				}//异常
				else {
					result.put("errorMessage", res==null ? "" : res.toString());
				}
				//清除缓存
				if(!"1".equals(jsonMap==null ? adapter.getParameter("stayCache") : (String) jsonMap.get("stayCache"))) {
					CacheManager.clear(asyncId, ASYNC_CACHE_KEY);
					CacheManager.clear(asyncId+"Data", ASYNC_CACHE_KEY);
				}
			}
		}
		//添加状态
		result.put("complete", state);
		result.put("asyncId", asyncId);
		//返回结果
		return result;
	}
	
	/**
	 * 提交异步任务
	 * @param asyncId 异步ID
	 */
	private static void asyncSubmit(String asyncId, Object instance, Async async, Adapter<ParamValidator> adapter, Method method, boolean withAdapter) {
		AsyncThreadPoolUtils.submitTask(()->{
			try {
				// 保存HttpServletRequest和HttpServletResponse对象
				FrameUtils.getServletRequestContainer().set(adapter.getRequest());
				FrameUtils.getServletResponseContainer().set(adapter.getResponse());
				//结果容器
				Object result;
				//状态默认已完成
				int state = 1;
				try {
					//验证器校验参数
					validParameter(adapter, method.getParameters());
					//校验不通过
					if(!adapter.getParamValidateErrorMsg().isEmpty()) {
						//保存状态
						state = 2;
						//保存信息
						result = String.join("；", adapter.getParamValidateErrorMsg().values());
					}//调用原方法
					else {
						result = withAdapter ? method.invoke(instance, adapter) : method.invoke(instance);
					}
				}catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
					// 保存状态
					state = 3;
					//保存信息
					result = e instanceof InvocationTargetException ? ((InvocationTargetException) e).getTargetException().getMessage() : e.getMessage();
					//写入日志
					Logger.error(e);
				}
				//将最终结果写入缓存
				CacheManager.set(asyncId+"Data", result, async.cacheTime() * 60, ASYNC_CACHE_KEY);
				CacheManager.set(asyncId, state, async.cacheTime() * 60, ASYNC_CACHE_KEY);
			} finally {
				// 移除HttpServletRequest和HttpServletResponse对象
				FrameUtils.getServletRequestContainer().remove();
				FrameUtils.getServletResponseContainer().remove();
			}
		});
	}

	/**
	 * 格式化参数，解析流参数
	 */
	private static void formatAguments(Adapter<ParamValidator> adapter) {
		// 过滤上传类型请求
		if (!Uploader.isUpload(adapter.getRequest())) {
			// 获取流参数
			String stream = getStreamParam(adapter.getRequest());
			// 判断是否满足JOSN格式
			if (StringUtils.isOuterMatched(stream, "{", "}") || StringUtils.isOuterMatched(stream, "[", "]")) {
				adapter.setArgumentJSONString(stream);
			}
			// 首先读取URL链接上的参数，优先级低于流
			adapter.getRequest().getParameterMap().forEach((key, values) -> {
				// 有值
				if (values != null) {
					// 处理key（数组参数去掉[]）
					key = key.endsWith("[]") ? key.substring(0, key.length() - 2) : key;
					// 没有值，给默认空字符串
					if (values.length == 0) {
						adapter.addParameter(key, "");
					} // 有值
					else {
						// 遍历依次写入容器并解码
						for (String v : values) {
							adapter.addParameter(key, CoderUtils.decodeURL(v));
						}
					}
				}
			});
			// 不是JSON，则手动解析。如果key相同，覆盖前面的参数
			if (!adapter.isJSONParam() && !stream.isEmpty()) {
				// 分割并遍历参数
				for (String kvStr : stream.split("&")) {
					// 等号分隔符
					int idx = kvStr.indexOf('=');
					// 存在
					if (idx > -1) {
						// key
						String key = CoderUtils.decodeURL(kvStr.substring(0, idx));
						// 处理key
						key = key.endsWith("[]") ? key.substring(0, key.length() - 2) : key;
						// 保存值
						adapter.addParameter(key, CoderUtils.decodeURL(kvStr.substring(idx + 1)));
					}
				}
			}
		}
	}

	/**
	 * 读取流参数，不解码
	 * @param request 请求对象
	 */
	private static String getStreamParam(HttpServletRequest request) {
		try {
			return FileUtils.readAll(request.getInputStream(), Charsets.UTF_8);
		} catch (IOException e) {
			throw new BaseException("参数读取失败", e);
		}
	}

	/**
	 * 检查URL后缀
	 */
	private static String checkUrlSuffix(String uri) {
		// 获取后缀配置
		String[] urlSuffixes = FrameConfigure.getUrlSuffixes();
		// 遍历配置
		for (String urlSuffix : urlSuffixes) {
			// 如果以指定后缀结尾
			if (uri.endsWith(urlSuffix)) {
				// 去掉后缀
				return uri.substring(0, uri.indexOf(urlSuffix));
			}
		}
		return uri;
	}

	/**
	 * 解析参数注解
	 * @param method 调用的方法
	 * @param arguments 参数列表
	 * @param adapter 适配器，此处用来保存解析后的参数
	 */
	private static void parseAnnotation(Method method, List<String> arguments, Adapter<ParamValidator> adapter) {
		// 获得注解的对象
		Arguments args = method.getAnnotation(Arguments.class);
		// 如果存在注解对象，则解析注解
		if (args != null) {
			// 注解类型
			int type = args.type();
			// 参数依次解析为数组
			switch (type) {
				case Arguments.TYPE_NONAME:
					// 保存参数
					adapter.setParameter("arguments_myframe", arguments);
					break;
				case Arguments.TYPE_WITHNAME:
					// 参数名
					String[] argnames = args.argnames();
					// 实际长度和溢出量
					int len = Math.min(argnames.length, arguments.size()), out = argnames.length - arguments.size();
					// 遍历公共长度
					for (int i = 0; i < len; i++) {
						adapter.addParameter(argnames[i], arguments.get(i));
					}
					// 若参数名称更长，未赋值的参数赋值为null
					if (out > 0) {
						for (int i = len, offset = len + out; i < offset; i++) {
							adapter.addParameter(argnames[i], null);
						}
					}
					break;
				case Arguments.TYPE_KEYVALUE:
					// 计数
					int i = 0, size = arguments.size();
					// 循环遍历
					while (i < size - 1) {
						// 键和值
						String key = arguments.get(i), value = arguments.get(i + 1);
						// 添加值
						adapter.addParameter(key, value);
						// 跳过已经保存的一对键和值
						i += 2;
					}
					break;
			}
		} else {
			// 保存参数
			adapter.setParameter("arguments_myframe", arguments);
		}
	}
}
